package com.xxx.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import com.xxx.bean.es.Book;
import com.xxx.util.EsUtil;

@RestController
@RequestMapping("/")
public class ESController extends BaseController {
	
	private static final Logger logger = LoggerFactory.getLogger(ESController.class);

    /**
     * 测试ES
     * http://localhost:80/testES
     */
    @RequestMapping("/testES")
    public ModelAndView testES(){
    	logger.info("enter method testES().");
    	ModelAndView mav = new ModelAndView(new MappingJackson2JsonView());
    	
    	/**********************1.创建索引*************************/
    	// 创建索引：索引名为testIndex1，别名为testAlias， 5个分片，每个分片2个副本
//    	EsUtil.createIndex("testindex2", "testalias2", 5, 2);
    	
    	/**********************2.添加文档*************************/
    	// 方式一：文档以json字符串形式提供
//    	String jsonStr = "{" +
//                "\"user\":\"testuser\"," +
//                "\"postDate\":\"2021-02-02\"," +
//                "\"message\":\"hello elasticsearch\"" +
//                "}";
//    	EsUtil.addIndex1("testindex2", "type1", jsonStr);
    	
    	// 方式二：文档以map形式提供
    	// 添加普通字段索引
//    	Map<String, Object> map = new HashMap<>();
//    	map.put("user", "guwq");
//    	map.put("postDate", "2021-02-02");
//    	map.put("message", "测试转码");
//    	EsUtil.addIndex2("testindex2", "testindex2-type2", map);
    	// 添加stored_field类型字段的索引（stored_field除了在source里存储，还会单独存储一份）
//    	map.put("title", "study python && java");
//    	map.put("content", "a good python && java lesson");
//    	map.put("date", "1550457070956");
//    	EsUtil.addIndex2("my_index", "my_type", map);

    	/**********************3.查询文档*************************/
    	// 根据文档ID查询文档
//    	String[] includes = new String[2];
//    	includes[0] = "user";
//    	includes[1] = "message";
//    	String[] excludes = new String[1];
//    	excludes[0] = "user";
//    	result = EsUtil.getById("", "esLive", "1", includes, excludes);
    	
    	// 检索stored_field字段
//    	result = EsUtil.getByStoredField("my_index", "my_type", "1", "title"); // 可以搜索出结果
//    	result = EsUtil.getByStoredField("my_index", "my_type", "2", "content"); // 搜索不到结果（content字段并没有存储到stored_field里）

    	/**********************4.删除文档*************************/
    	// 根据文档ID删除文档
//    	EsUtil.deleteById("my_index", "my_type", "1");

    	/**********************5.更新文档*************************/
    	// 根据文档ID更新文档
    	// 方式一：入参传json
//    	String jsonStr = "{" +
//    				"\"content\":\"JAVA语法1\"" +
//    			"}";
//    	EsUtil.updateById("my_index", "my_type", "2", jsonStr);
    	// 方式二：入参传map
//    	Map<String, Object> map = new HashMap<>();
//    	map.put("title", "JAVA语法");
//    	map.put("content", "JAVA语法234");
//    	EsUtil.updateById2("my_index", "my_type", "2", map);

    	/**********************6.搜索文档*************************/
    	QueryBuilder queryBuilder = null;
    	
    	/**********************6.1 matchQuery*************************/
    	// matchQuery：会将搜索词分词，再与目标查询字段进行匹配，若分词中的任意一个词与目标字段匹配上，则可查询到。
    	// 搜索场景之——matchAllQuery：搜索全部文档
    	queryBuilder = QueryBuilders.matchAllQuery(); // 搜索结果返回全部记录
    	List<Book> list = EsUtil.search("my_index", "my_type", queryBuilder, Book.class, 0, 10, null, null, null);
    	mav.addObject("list", list);
    	
    	// 搜索场景之——matchQuery(String name, Object text)：单个字段匹配字段名为name,值为text的文档
//    	queryBuilder = QueryBuilders.matchQuery("title", "java"); // 搜索结果返回title中包含“java”的记录（包括JAVA）
//    	queryBuilder = QueryBuilders.matchQuery("title", "入门"); // 搜索结果返回title中包含“入门”的记录
//    	queryBuilder = QueryBuilders.matchQuery("content", "java"); // 搜索结果返回content中包含“java”的记录（包括JAVA）
//    	List<Book> list = EsUtil.search("my_index", "my_type", queryBuilder, Book.class, 0, 10, null, null, null);
//    	mav.addObject("list", list);
    	
    	// 搜索场景之——multiMatchQuery(Object text, String... fieldNames)：在多个字段中匹配text
    	// 即从多个字段中搜索，当fieldNames中只有一个字段时，作用和matchQuery相同
//    	queryBuilder = QueryBuilders.multiMatchQuery("python", "title", "content"); // 搜索结果返回title和content字段里包含“python”的记录
//    	queryBuilder = QueryBuilders.multiMatchQuery("python", "content"); // 搜索结果里只返回content字段里包含“python”的记录
//    	List<Book> list = EsUtil.search("my_index", "my_type", queryBuilder, Book.class, 0, 10, null, null, null);
//    	mav.addObject("list", list);
    	// multiMatchQuery包含5种类型的匹配（type属性）：best_fields（默认）、most_fields、cross_fields、phrase、phrase_prefix
    	// best_fields（默认）：查找与任何字段匹配的文档，但只取所有字段中的max（score），字段完全匹配的文档的评分最高。
		// most_fields：查找与任何字段匹配的文档，计算所有字段的sum（score），越多字段匹配的评分越高。
		// cross_fields：跨字段查询，将搜索词条的不同分词词汇分配到不同字段中去匹配。
		// phrase：在每个字段上均执行 match_phrase查询，并将最佳字段的 _score作为结果返回。
		// phrase_prefix：在每个字段上均执行 match_phrase_prefix查询，并将最佳字段的 _score作为结果返回。
    	
    	// multiMatchQuery的字段加权，默认每个字段的权重是1.0
//    	MultiMatchQueryBuilder multiMatchQueryBuilder = new MultiMatchQueryBuilder("python", "title", "content"); // 搜索结果返回title和content里包含“python”的记录
//    	multiMatchQueryBuilder.field("title", 3.0f); // 设置title字段的权重boost是3.0
//    	multiMatchQueryBuilder.operator(Operator.AND); // Operator默认是or
//    	List<Book> list = EsUtil.search("my_index", "my_type", multiMatchQueryBuilder, Book.class, 0, 10, null, null, null);
//    	mav.addObject("list", list);
    	
    	// 搜索场景之——matchPhraseQuery(String name, Object text)：短语搜索
    	// 即短语搜索，text不会被分词，而是直接以一个字符串整体的短语形式查询 
    	// 第一个参数name是field name，第二个参数text是搜索短语
//    	queryBuilder = QueryBuilders.matchPhraseQuery("title", "java"); // 搜索结果返回title中包含英文单词“java”的记录（包括JAVA）
//    	queryBuilder = QueryBuilders.matchPhraseQuery("title", "跟我学java");  // 搜索结果返回title中包含“跟我学java”的记录（短语不分词）
//    	queryBuilder = QueryBuilders.matchPhraseQuery("content", "java"); // 搜索结果返回content字段里包含“java”的记录
//    	queryBuilder = QueryBuilders.matchPhraseQuery("content", "系列"); // 搜索结果返回content字段里包含“系列”的记录
//    	queryBuilder = QueryBuilders.matchPhraseQuery("content", "软件工程师"); // 搜索结果返回content字段里包含“软件工程师”的记录
//    	queryBuilder = QueryBuilders.matchPhraseQuery("title", "study java");  // 没有搜索到记录
//    	List<Book> list = EsUtil.search("my_index", "my_type", queryBuilder, Book.class, 0, 10, null, null, null);
//    	mav.addObject("list", list);
    	
    	// slop因子在matchPhraseQuery里的作用：是指两个项的位置之间允许的最大间隔距离
//    	queryBuilder = QueryBuilders.matchPhraseQuery("title", "study java"); //搜索不到结果
//    	MatchPhraseQueryBuilder matchPhraseQueryBuilder = new MatchPhraseQueryBuilder("title", "study java");
//    	matchPhraseQueryBuilder.slop(1); // 设置slop属性（默认是0），&&符号不会被分词
    	// 搜索结果返回title字段里包含"study java"短语的记录
//    	List<Book> list = EsUtil.search("my_index", "my_type", matchPhraseQueryBuilder, Book.class, 0, 10, null, null, null);
//    	mav.addObject("list", list);
    	
    	// 搜索场景之——matchPhrasePrefixQuery(String name, Object text)：
    	// 即短语前缀搜索，一般用在查询时输入即搜索（即时搜索）。第一个参数name是field name，第二个参数text是搜索短语
//    	queryBuilder = QueryBuilders.matchPhrasePrefixQuery("title", "java语法"); // 搜索结果返回title字段中以“java语法”开头的记录
//    	queryBuilder = QueryBuilders.matchPhrasePrefixQuery("title", "跟我学"); // 搜索结果返回title字段中以“跟我学”开头的记录（输入搜索条件时预先展示搜索词条场景会用到）
//    	List<Book> list = EsUtil.search("my_index", "my_type", queryBuilder, Book.class, 0, 10, null, null, null);
//    	mav.addObject("list", list);
    	// ※match_phrase_prefix 和 match_phrase 用法是一样的，区别就在于它允许对最后一个词条前缀匹配。
    	// maxExpansions在matchPhrasePrefixQuery里的作用：前缀搜索是非常消耗资源的操作，例如输入一个a，可能会匹配很多条记录，但是很多都不是想要，此时可以通过设置maxExpansions属性来控制前缀扩展数量（默认50）
//    	MatchPhrasePrefixQueryBuilder matchPhrasePrefixQueryBuilder = new MatchPhrasePrefixQueryBuilder("content", "good");
//    	MatchPhrasePrefixQueryBuilder matchPhrasePrefixQueryBuilder = new MatchPhrasePrefixQueryBuilder("title", "我");
//    	matchPhrasePrefixQueryBuilder.maxExpansions(2);
//    	List<Book> list = EsUtil.search("my_index", "my_type", matchPhrasePrefixQueryBuilder, Book.class, 0, 10, null, null, null);
//    	mav.addObject("list", list);
    	
    	
    	/**********************6.2 wildcardQuery*************************/
    	// 即通配符匹配或者叫模糊匹配，?匹配单个字符，*匹配多个字符
    	// wildcardQuery 是 term 级别的 query，搜索的value必须在term里存在
    	// 注意该查询比较消耗资源需要在很多索引词上重复执行，所以为了提高执行效率，通配符索引词不应该以通配符开头。
//    	WildcardQueryBuilder wildcardQueryBuilder = null;
//    	wildcardQueryBuilder = QueryBuilders.wildcardQuery("title", "*java*"); // 搜索结果返回title包含“java”这个term的记录
//    	wildcardQueryBuilder = QueryBuilders.wildcardQuery("title", "*跟我学*"); // 搜索结果为空（因为没有“跟我学”这个term）
//    	List<Book> list = EsUtil.search("my_index", "my_type", wildcardQueryBuilder, Book.class, 0, 10, null, null, null);
//    	mav.addObject("list", list);

    	
    	/**********************6.3 prefixQuery*************************/
    	// 前缀查询，匹配包含特定前缀的索引词（不分词）
//    	queryBuilder = QueryBuilders.prefixQuery("title", "java"); // 搜索结果返回title字段包含“java”的记录
//    	queryBuilder = QueryBuilders.prefixQuery("title", "jav"); // 搜索结果返回title字段包含“jav”的记录
//    	queryBuilder = QueryBuilders.prefixQuery("title", "ava"); // 搜索结果为空（因为title字段里没有以“ava”开头的记录）
//    	List<Book> list = EsUtil.search("my_index", "my_type", queryBuilder, Book.class, 0, 10, null, null, null);
//    	mav.addObject("list", list);

    	
    	/**********************6.4 termQuery*************************/
    	// 即精确查询，不会对搜索词进行分词处理，而是作为一个整体与目标字段进行匹配，若完全匹配，则可查询到。
    	// 通常用来匹配不分词的field搜索，如：订单ID等
    	// 先创建不分词的索引（my_index1）
    	// 批量添加索引
//    	map.put("id", "id333333333");
//    	map.put("title", "Python入门");
//    	map.put("content", "Python入门简介");
//    	EsUtil.addIndex2("my_index1", "my_type1", map);
//    	map.put("id", "id44444444");
//    	map.put("title", "Python语法");
//    	map.put("content", "Python语法详解");
//    	EsUtil.addIndex2("my_index1", "my_type1", map);
    	// 搜索场景之——term分词精确搜索
//    	queryBuilder = QueryBuilders.termQuery("id", "id333333333"); // 能搜索出结果
//    	queryBuilder = QueryBuilders.termQuery("id", "id3333333"); // 不能搜索出结果（不分词字段必须完全匹配才能搜索出来结果）
//    	queryBuilder = QueryBuilders.termQuery("title", "Python入门"); // 不能搜索出结果
//    	boolQueryBuilder.must(queryBuilder);
//    	List<Book1> list = EsUtil.search("my_index1", "my_type1", boolQueryBuilder, Book1.class, 0, 10, null, null, null);
//    	mav.addObject("list", list);
    	
    	// terms query：相当于数据库里的 in 查询
//    	queryBuilder = QueryBuilders.termsQuery("id", "id333333333", "id44444444", "id55555"); // 能搜索出结果（没匹配的忽略）
//    	List<Book1> list = EsUtil.search("my_index1", "my_type1", queryBuilder, Book1.class, 0, 10, null, null, null);
//    	mav.addObject("list", list);
    	
    	
    	/**********************6.5 fuzzyQuery*************************/
    	// 即模糊搜索，或者叫近似搜索，例如：本想搜索java，但是输入了Jave，也需要将java的记录搜索出来
//    	queryBuilder = QueryBuilders.fuzzyQuery("title", "pythmp"); // 搜索jave，也会返回java的搜索结果
    	// fuzzyQuery有一个fuzziness属性用来控制模糊性程度的，fuzziness的含义是检索的term前后增加或减少n个单词的匹配查询
//    	queryBuilder = QueryBuilders.fuzzyQuery("title", "pythmp").fuzziness(Fuzziness.TWO); 
//    	List<Book> list = EsUtil.search("my_index", "my_type", queryBuilder, Book.class, 0, 10, null, null, null);
//    	mav.addObject("list", list);
    	
    	
    	/**********************6.6 rangeQuery*************************/
    	// 即范围查询，一般用在时间，数字等类型的字段上
    	// gt：开区间开始；lt：开区间截止
//    	queryBuilder = QueryBuilders.rangeQuery("date").gt("1550457050856");
//    	queryBuilder = QueryBuilders.rangeQuery("date").to("1550457050856");
    	// from：闭区间开始； to：闭区间截止
//    	queryBuilder = QueryBuilders.rangeQuery("date").from("1550457050856");
//    	queryBuilder = QueryBuilders.rangeQuery("date").to("1550457050856");
    	// gte：闭区间开始，等价于from；lte：闭区间截止，等价于to；
//    	queryBuilder = QueryBuilders.rangeQuery("date").gte("1550457050856");
//    	queryBuilder = QueryBuilders.rangeQuery("date").lte("1550457050856");
//    	List<Book> list = EsUtil.search("my_index", "my_type", queryBuilder, Book.class, 0, 10, null, null, null);
//    	mav.addObject("list", list);

    	
    	/**********************6.7 booleanQuery*************************/
    	// 布尔查询，或者叫逻辑查询，must为必须，should为不必须，must_not为不能
    	// 搜索场景之——复合搜索：使用BoolQueryBuilder绑定多个条件进行搜索（and）
    	// 搜索title字段里包含“java”，且content字段里包含“软件”的记录
//    	BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); // 布尔查询
//    	queryBuilder = QueryBuilders.matchQuery("title", "java");
//    	boolQueryBuilder.must(queryBuilder); // 绑定其中一个搜索条件
//    	queryBuilder = QueryBuilders.matchQuery("content", "软件");
//    	boolQueryBuilder.must(queryBuilder); // 绑定另一个搜索条件
//    	List<Book> list2 = EsUtil.search("my_index", "my_type", boolQueryBuilder, Book.class, 0, 10, null, null, null);
//    	mav.addObject("list", list2);
    	// 也可以将多个matchQuery封装到map里
    	// 比较booleanQuery和disMaxQuery的区别：
//    	BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//    	queryBuilder = QueryBuilders.termQuery("title", "java1");
//    	boolQueryBuilder.should(queryBuilder); // 绑定其中一个搜索条件
//    	queryBuilder = QueryBuilders.termQuery("content", "python");
//    	boolQueryBuilder.should(queryBuilder); // 绑定另一个搜索条件
//    	List<Book> list1 = EsUtil.search("my_index", "my_type", boolQueryBuilder, Book.class, 0, 10, null, null, null);
//    	mav.addObject("list1", list1);
//    	DisMaxQueryBuilder disMaxQueryBuilder = QueryBuilders.disMaxQuery();
//    	disMaxQueryBuilder.add(QueryBuilders.termQuery("title", "java1"));
//    	disMaxQueryBuilder.add(QueryBuilders.termQuery("content", "python"));
//    	List<Book> list2 = EsUtil.search("my_index", "my_type", disMaxQueryBuilder, Book.class, 0, 10, null, null, null);
//    	mav.addObject("list2", list2);
    	
    	
    	/**********************6.8 disMaxQuery *************************/
    	// Disjunction Max Query 分离最大化查询，将子查询union到一起，每个文档的分数是子查询中相同文档的得分最大值。
//    	DisMaxQueryBuilder disMaxQueryBuilder = QueryBuilders.disMaxQuery();
//    	disMaxQueryBuilder.add(QueryBuilders.termQuery("title", "跟我学"));
//    	disMaxQueryBuilder.add(QueryBuilders.termQuery("content", "python"));
//    	disMaxQueryBuilder.add(QueryBuilders.termQuery("title", "python"));
//    	List<Book> list = EsUtil.search("my_index", "my_type", disMaxQueryBuilder, Book.class, 0, 10, null, null, null);
//    	mav.addObject("list", list);
    	
    	/**********************6.9 highlight search*************************/
    	// 高亮搜索，搜索结果里高亮展示指定字段
    	// 包含3种类型的高亮搜索：highlighter（默认）、fast-vector-highlighter、postings-hightlighter
//    	HighlightBuilder highlightBuilder = new HighlightBuilder();
//		highlightBuilder.preTags("<span style=\"color:red\">"); // 高亮字段展示样式
//		highlightBuilder.postTags("</span>");
//		queryBuilder = QueryBuilders.matchQuery("title", "java");
//    	List<Book1> list = EsUtil.searchWithHighlight("my_index", "my_type", queryBuilder, Book1.class, 0, 10, 
//    			null, null, null, highlightBuilder, "title"); // 搜索结果返回title字段里包含“java”的记录，其中结果中的关键字“java”高亮显示
//    	mav.addObject("list", list);
    	
        return mav;
    }
}
